/**
 * @file bib.c
 * @brief Implementazione della libreria per le schede bibliografiche
 *  Si dichiara che il contenuto di questo file e' in ogni sua parte opera originale dell' autore.
 * @author Marco Luisi
 */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bib.h"
#include "biblio.h"

/** @name Funzioni di utilità per la libreria bib aggiunte dallo studente.
 *  Funzioni per la gestione degli autori, funzioni per il parsing, funzioni per la comparazione tra schede.
 *  Tutte funzioni static in quanto utilizzate solo dalla libreria bib.
 */
/**@{*/

/**
 * Aggiunge un nuovo autore alla lista di autori della scheda
 *
 * @param  a puntatore alla lista di autori
 * @param  c cognome dell'autore
 * @param  n nome dell'autore
 * @return puntatore al nuovo autore, NULL in caso di errore
 */
static autore_t* new_autore(autore_t **a, char *c, char *n);

/**
 * Elimina tutti gli autori della lista autori passata per riferimento
 * @param a puntatore alla prima struct autore della lista da eliminare
 */
static void free_autore(autore_t *a);

/**
 * Taglia gli spazi all'inizio e alla fine della stringa
 * @param string stringa da trimmare
 * @return start puntatore al primo carattere non spazio della stringa
 */
static char* trim(char* string);

/**
 * Effettua il parsing del record restituendo in caso di record ben formattato una nuova scheda bibliografica;
 * scompone la stringa in token che vengono passati alla funzione field_store().
 * @param rec record da analizzare
 * @param s puntatore alla scheda in cui memorizzare i campi del record
 * @return esito del parsing
 */
static int parse(char* rec, scheda_t* s);

/**
 * Funzione di match dei nomi dei campi del record.
 * @param token
 * @return codice del campo corrispondente o codice d'errore.
 */
static int field_match(char *);

/**
 * Controlla la validità del valore per il campo e salva il valore nel corrispondente campo della struct scheda.
 * @param field codice del campo
 * @param token token da analizzare
 * @param scheda puntatore alla scheda in cui salvare il valore del campo
 * @return 1 in caso di campo obbligatorio salvato, 0 se campo non obbligatorio, -1 in caso di errore
 */
static int field_store(int field, char *token, scheda_t *scheda);

/*Funzioni di comparazione delle schede passate alla funzione di libreria qsort*/

/**
 * Compara le schede in base agli autori: scorre la lista autori fino a che non trova differenze.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int autore_cmp(const void *p1, const void *p2);

/**
 * Compara le schede in base al titolo.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int titolo_cmp(const void *p1, const void *p2);

/**
 * Compara le schede in base all'editore.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int editore_cmp(const void *p1, const void *p2);

/**
 * Compara le schede in base al luogo di pubblicazione.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int luogo_cmp(const void *p1, const void *p2);

/**
 * Compara le schede in base all'anno di pubblicazione.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int anno_cmp(const void *p1, const void *p2);

/**
 * Compara le schede in base alla collocazione.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int colloc_cmp(const void *p1, const void *p2);

/**
 * Compara le schede in base alla descrizione.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int descr_cmp(const void *p1, const void *p2);

/**
 * Compara le schede in base alla nota.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int nota_cmp(const void *p1, const void *p2);

/**
 * Compara le schede alla data di termine del prestito.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int prestito_cmp(const void *p1, const void *p2);

/**@}*/


/** crea ed inizializza una nuova scheda
 \returns res la nuova scheda (allocata all'interno della funzione)
 \returns NULL se ci sono stati problemi (setta errno)
 */
scheda_t * new_scheda(void) {
	scheda_t *res = NULL;
	MALLOC(res, sizeof(scheda_t));
	res->prestito.disponibile = TRUE;
	res->autore = NULL;
	return (res);
}

/** libera la memoria occupata da una scheda

 \param ps puntatore al puntatore alla scheda (*ps viene settato a NULL dalla funzione)
 */
void free_scheda(scheda_t ** ps) {
	if (ps == NULL || *ps == NULL) {
		return;
	}
	free_autore((*ps)->autore);
	free(*ps);
	*ps = NULL;
}

/** confronta due schede
 \param s1 puntatore alla prima scheda
 \param s2 puntatore alla seconda scheda

 \returns TRUE se le schede sono uguali (tutti i campi sono uguali) e
 \returns FALSE altrimenti
 */
int is_equal_scheda(scheda_t * s1, scheda_t * s2) {
	autore_t *a1;
	autore_t *a2;
	if (s1 == NULL || s2 == NULL) {
		return (FALSE);
	}
	if (strcmp(s1->collocazione, s2->collocazione) != 0) {
		return (FALSE);
	}
	if (strcmp(s1->descrizione_fisica, s2->descrizione_fisica) != 0) {
		return (FALSE);
	}
	if (strcmp(s1->nota, s2->nota) != 0) {
		return (FALSE);
	}
	if (strcmp(s1->titolo, s2->titolo) != 0) {
		return (FALSE);
	}

	a1 = s1->autore;
	a2 = s2->autore;
	/*Scorre la lista degli autori della prima scheda*/
	while (a1 != NULL) {
		if (a2 == NULL) {
			return (FALSE);
		}
		if (strcmp(a1->cognome, a2->cognome) != 0) {
			return (FALSE);
		}
		if (strcmp(a1->nome, a2->nome) != 0) {
			return (FALSE);
		}
		a1 = a1->next;
		a2 = a2->next;
	}
	/*Se al termine della lista autori della prima scheda si trovano ulteriori autori per la seconda*/
	if ((a1 == NULL) & (a2 != NULL)) {
		return (FALSE);
	}
	/*Flag disponibile diverso*/
	if (s1->prestito.disponibile != s2->prestito.disponibile) {
		return (FALSE);
	}
	/*Se non sono disponibili si controlla la data di scadenza del prestito*/
	if (s1->prestito.disponibile == FALSE) {
		if (((s1->prestito.scadenza).tm_mday != s2->prestito.scadenza.tm_mday)
				&& (s1->prestito.scadenza.tm_mon != s2->prestito.scadenza.tm_mon)
				&& (s1->prestito.scadenza.tm_year
						!= s2->prestito.scadenza.tm_year)) {
			return (FALSE);
		}
		/*Stessa data di scadenza del prestito*/
	}

	if (strcmp(s1->pub.editore, s2->pub.editore) != 0) {
		return (FALSE);
	}
	if (strcmp(s1->pub.luogo, s2->pub.luogo) != 0) {
		return (FALSE);
	}
	if (s1->pub.anno != s2->pub.anno) {
		return (FALSE);
	}
	return (TRUE);
}

/**  FORMATO RECORD della scheda relativa ad un volume (usato per la stampa e la registrazione su file)
 autore: Cognome/i (char*) , (virgola separatore) Nome/i (char*)
 titolo: stringa del titolo (char*)
 editore: nome editore (char*)
 luogo_pubblicazione: luogo pubblicazione (char*)
 anno: anno di 4 cifre (char*)
 prestito: data di restituzione del prestito gg-mm-aaaa (char *)
 se il campo e' assente il libro e' disponibile al prestito
 collocazione: stringa che rappresenta la collocazione dentro la biblioteca
 (char *)
 descrizione_fisica: stringa che descrive le caratteristiche fisiche del libro
 (numero di pagine altezza, illustrazioni etc ) (char*)
 nota: stringa di commento (char*)

 ************************************
 NOTE:
 1) i campi sono separati da ';' la scheda terminata da '\n'
 2) nelle stringhe non puo' comparire il separatore (;) e nella stringa nome e cognome dell'autore non puo' comparire nemmeno la virgola (,) ;
 3) possono essere presenti spazi bianchi all'inizio ed alla fine di un campo (devono essere ignorati)
 4) i campi si possono trovare in un ordine qualsiasi! Tuttavia l'ordine sopra descritto e' quello richiesto nelle funzioni di stampa (print_scheda()) e di serializzazione (scheda_to_record())
 */

/** stampa la scheda in formato ECORD (vedi sopra) sullo stream specificato

 \param f stream di output
 \param s puntatore alla scheda
 */
void print_scheda(FILE* f, scheda_t * s) {
	autore_t *aut = NULL;
	if (f == NULL || s == NULL) {
		errno = EINVAL; /*Utile per store_records()*/
		return;
	}
	/*Stampa degli autori*/
	aut = s->autore;
	while (aut != NULL) {
		fprintf(f, "autore: %s, %s; ", aut->cognome, aut->nome);
		aut = aut->next;
	}
	/*Stampa dei dati del libro*/
	fprintf(f, "titolo: %s; ", s->titolo);
	fprintf(f, "editore: %s; ", s->pub.editore);
	fprintf(f, "luogo_pubblicazione: %s; ", s->pub.luogo);
	fprintf(f, "anno: %d; ", s->pub.anno);
	if (s->prestito.disponibile == FALSE) {
		fprintf(f, "prestito: %d-%d-%d; ", s->prestito.scadenza.tm_mday,
				s->prestito.scadenza.tm_mon + 1,
				s->prestito.scadenza.tm_year + 1900);
	}
	fprintf(f, "collocazione: %s; ", s->collocazione);
	fprintf(f, "descrizione_fisica: %s; ", s->descrizione_fisica);
	fprintf(f, "nota: %s;", s->nota);
	fprintf(f, "\n");
}

/** trasforma un record in una scheda
 \param r record da trasformare (non viene modificato)
 \param l lunghezza massima del record (serve ad evitare l'overrun ...)

 \returns res la nuova scheda (allocata all'interno della funzione)
 \returns NULL se ci sono stati problemi (setta errno)
 errno=EINVAL per stringa mal formattata
 */
scheda_t* record_to_scheda(char* r, int l) {
	char *tmp = NULL;
	scheda_t *scheda;

	if (r == NULL) {
		errno = EINVAL;
		return (NULL);
	}
	/*Copia temporanea del record che verrà analizzata da parse, necessaria perchè strtok modifica la stringa*/
	tmp = strndup(r, l);
	if (tmp == NULL) {
		errno = ENOMEM;
		return (NULL);
	}
	scheda = new_scheda();
	if (scheda == NULL) {
		return (NULL);
	}
	if (parse(tmp, scheda) == -1) {
		errno = EINVAL;
		free_scheda(&scheda);
		free(tmp);
		return (NULL);
	}
	free(tmp);
	return (scheda);
}

/** trasforma una scheda in un record
 \param s la scheda da trasformare

 \returns r il puntatore al record che rappresenta la scheda (allocato all'interno della funzione)
 \returns  NULL se ci sono stati problemi (setta errno)
 */
char * scheda_to_record(scheda_t* s) {
	char *r;
	autore_t *aut;
	int aut_num = 0;
	int aut_len;
	int offset = 0;
	int chars = 0;
	NULL_PARAM(s);
	/*Conteggio degli autori*/
	aut = s->autore;
	while (aut) {
		aut_num++;
		aut = aut->next;
	}
	/*Allocazione del record*/
	aut_len = (LTAG + LCOGN + LNOME) * aut_num;
	MALLOC(r,
			(LLSTRING * 2 + LSSTRING * 5 + LANNO + aut_len + LTAG * (NCAMPI - 1)));
	/*Stampa degli autori*/
	aut = s->autore;
	while (aut) {
		chars = sprintf(&r[offset], "autore: %s, %s; ", aut->cognome,
				aut->nome);
		if (chars < 0) {
			free(r);
			return (NULL);
		}
		offset += chars;
		aut = aut->next;
	}
	/*Stampa dei rimanenti campi del record*/
	chars = sprintf(&r[offset], "titolo: %s; ", s->titolo);
	if (chars < 0) {
		free(r);
		return (NULL);
	}
	offset += chars;
	chars = sprintf(&r[offset], "editore: %s; ", s->pub.editore);
	if (chars < 0) {
		free(r);
		return (NULL);
	}
	offset += chars;
	chars = sprintf(&r[offset], "luogo_pubblicazione: %s; ", s->pub.luogo);
	if (chars < 0) {
		free(r);
		return (NULL);
	}
	offset += chars;
	chars = sprintf(&r[offset], "anno: %d; ", s->pub.anno);
	if (chars < 0) {
		free(r);
		return (NULL);
	}
	offset += chars;
	if (s->prestito.disponibile == FALSE) {
		chars = sprintf(&r[offset], "prestito: %d-%d-%d; ",
				s->prestito.scadenza.tm_mday, s->prestito.scadenza.tm_mon + 1,
				s->prestito.scadenza.tm_year + 1900);
		if (chars < 0) {
			free(r);
			return (NULL);
		}
		offset += chars;
	}
	chars = sprintf(&r[offset], "collocazione: %s; ", s->collocazione);
	if (chars < 0) {
		free(r);
		return (NULL);
	}
	offset += chars;
	chars = sprintf(&r[offset], "descrizione_fisica: %s; ",
			s->descrizione_fisica);
	if (chars < 0) {
		free(r);
		return (NULL);
	}
	offset += chars;
	chars = sprintf(&r[offset], "nota: %s;\n", s->nota);
		if (chars < 0) {
			free(r);
			return (NULL);
		}
	offset += chars;
	return (r);
}

/** legge tutti i record da un file e li memorizza in un array -- i record malformati vengono scartati ma se c'e' almeno un record convertibile corretamente la funzione ha successo

 \param fin stream da cui leggere i record
 \param psarray puntatore alla variabile che conterra' l'indirizzo dell'array di puntatori alle schede (allocato), significativo se n >0

 \returns -1 se si e' verificato un errore (setta errno)
 \returns n (n > 0) numero di record convertiti correttamente (ampiezza dell'array di schede) in questo caso *psarray contiene l'indirizzo dell'array allocato
 */
int load_records(FILE* fin, scheda_t** psarray[]) {
	char *buffer = NULL;
	int c;
	int count = 0;
	int i = 0;
	int records = 0;
	size_t sz;
	ssize_t read;
	scheda_t **schede;

	NULL_PAR_M1(fin);
	/*Conteggio dei record*/
	while ((c = fgetc(fin)) != EOF) {
		if (c == '\n')
			++count;
	}
	if (count == 0) {
		return (-1);
	}
	MALLOC_M1(schede, count * sizeof(scheda_t*));
	rewind(fin);
	/*Parsing dei record*/
	while (count > 0) {
		errno = 0;
		read = getline(&buffer, &sz, fin);
		if (read == -1) {
			return (-1);
		}
		schede[i] = record_to_scheda(buffer, sz);
		if (schede[i] != NULL) {
			records++;
			i++;
		}
		count--;
		free(buffer);
		buffer = NULL;
	}
	if (records == 0) {
		free(*schede);
		schede = NULL;
	}
	*psarray = schede;
	return (records);
}

/** scrive su file un array di schede sotto forma di record
 \param fout stream su cui scrivere i record
 \param sarray puntatore all'array di puntatori alle schede
 \param n numero schede nell'array

 \returns -1 se si e' verificato un errore (setta errno) -- nota l'errore nella scrittura di un singolo record viene riportato nel numero di record scritti, non provoca il ritorno di (-1)
 \returns n (n > 0) numero di record scritti correttamente */
int store_records(FILE* fout, scheda_t* sarray[], int n) {
	int i;
	int records = 0;
	NULL_PAR_M1(fout);
	for (i = 0; i < n; i++) {
		errno = 0;
		print_scheda(fout, sarray[i]);
		if(errno == 0){
		records++;
		}
	}
	return (records);
}

/**ordina i record nell'array relativamente al campo specificato. Gli ordinamenti definiti per i vari campi sono i seguenti:
 AUTORE -- lessicografico (Cognome Nome) sulla lista degli autori
 TITOLO, EDITORE, LUOGO_PUBBLICAZIONE, COLLOCAZONE, DESCRIZIONE_FISICA, NOTA -- Lessicografico sulla stringa
 ANNO -- crescente
 PRESTITO -- crescente

 Nota: per ordine Lessicografico si intende quello normalmente usato nella strcmp()

 \param sarray puntatore array da ordinare (viene modificato durante l'ordinamento)
 \param n lunghezza array
 \param c campo rispetto al quale ordinare

 \returns -1 se si e' verificato un errore (setta errno) (in questo caso s e' invariato)
 \returns 0 se tutto e' andato bene
 */
int sort_schede(scheda_t* sarray[], int n, campo_t c) {

	if (sarray == NULL || *sarray == NULL) {
		errno = EINVAL; 
		return (-1);
	}
	/*In base al campo sceglie la funzione di comparazione per il quicksort*/
	switch (c) {

	case (AUTORE): {
		qsort((void*) sarray, n, sizeof(scheda_t*), autore_cmp);
		break;
	}
	case (ANNO): {
		qsort(sarray, n, sizeof(scheda_t*), anno_cmp);
		break;
	}
	case (PRESTITO): {
		qsort(sarray, n, sizeof(scheda_t*), prestito_cmp);
		break;
	}
	case (TITOLO): {
		qsort(sarray, n, sizeof(scheda_t*), titolo_cmp);
		break;
	}
	case (EDITORE): {
		qsort(sarray, n, sizeof(scheda_t*), editore_cmp);
		break;
	}
	case (LUOGO_PUBBLICAZIONE): {
		qsort(sarray, n, sizeof(scheda_t*), luogo_cmp);
		break;
	}
	case (COLLOCAZIONE): {
		qsort(sarray, n, sizeof(scheda_t*), colloc_cmp);
		break;
	}
	case (DESCRIZIONE_FISICA): {
		qsort(sarray, n, sizeof(scheda_t*), descr_cmp);
		break;
	}
	case (NOTA): {
		qsort(sarray, n, sizeof(scheda_t*), nota_cmp);
		break;
	}
	default: {
		errno = EINVAL;
		return (-1);
	}
	}
	return (0);
}

/*Funzioni aggiunte*/

/**
 * Aggiunge un nuovo autore alla lista di autori della scheda.
 *
 * @param  a puntatore alla lista di autori
 * @param  c cognome dell'autore
 * @param  n nome dell'autore
 * @return puntatore al nuovo autore, NULL in caso di errore
 */
static autore_t* new_autore(autore_t **a, char *c, char *n) {
	autore_t *new;
	autore_t **tmp;
	tmp = a;
	/*Creazione nuovo autore*/
	MALLOC(new, sizeof(autore_t));
	new->next = NULL;
	snprintf(new->cognome, LCOGN, "%s", c);
	snprintf(new->nome, LNOME, "%s", n);
	/*E' il primo autore inserito nella scheda*/
	if (*a == NULL) {
		*a = new;
		return (new);
	}
	/*Altrimenti si scorre la lista degli autori fino alla prima posizione libera*/
	while (*tmp != NULL) {
		tmp = &((*tmp)->next);
	}
	*tmp = new;
	return (new);
}

/**
 * Elimina tutti gli autori della lista autori passata per riferimento.
 * @param a puntatore alla prima struct autore della lista da eliminare
 */
static void free_autore(autore_t *a) {
	if (a == NULL) {
		return;
	}
	if (a->next != NULL) {
		free_autore(a->next);
	}
	free(a);
	return;
}

/**
 * Taglia gli spazi all'inizio e alla fine della stringa
 * @param string stringa da trimmare
 * @return start puntatore al primo carattere non spazio della stringa
 */
char* trim(char* string) {
	char *start = string;
	char *end;
	/*Prima si saltano gli spazi a sinistra*/
	while (isspace(*start) != 0) {
		start++;
	}
	/*Poi si scorre all'indietro fino al primo spazio dopo l'ultimo carattere alfanumerico della stringa*/
	end = start + strlen(start) - 1;
	while ((isspace(*end) != 0)) {
		if (isspace(*(end-1)) == 0) {
			*end = '\0';
		}
		--end;
	}
	return (start);
}

/**
 * Effettua il parsing del record restituendo in caso di record ben formattato una nuova scheda bibliografica;
 * scompone la stringa in token che vengono passati alla funzione field_store().
 * @param rec record da analizzare
 * @param s puntatore alla scheda in cui memorizzare i campi del record
 * @return esito del parsing
 */
static int parse(char* string, scheda_t *s) {

	char *token;
	int fields = 0;
	int field;
	int parsed;

	/*Controllo preliminare sull'ultimo terminatore, dettato dalla implementazione di strtok;*/
	string = trim(string);
	if (string[strlen(string) - 1] != ';'
			&& string[strlen(string) - 2] != ';') {
		return (-1);
	}
	/*Si inizia a cercare un token corrispondente al nome di uno dei campi previsti e si controlla*/
	token = strtok(string, ":");
	if (token == NULL) {
		return (-1);
	}
	field = field_match(token);
	/*In base al campo ottenuto si cerca il prossimo token corrispondente al valore del campo*/
	switch (field) {

	case (-1): {
		return (-1);
		break;
	}

	case (AUTORE): {
		token = strtok(NULL, ",");
		break;
	}

	default: {
		token = strtok(NULL, ";");
		break;
	}
	}
	/*Controllo dell'esito e salvataggio del valore del campo nella scheda e si conteggia il campo*/
	parsed = field_store(field, token, s);
	if (parsed == -1) {
		return (-1);
	}
	fields += parsed;
	/*Continua ad ottenere token per campi e valori fino alla fine della stringa o all'insorgere di un errore nel parsing*/
	while (token) {
		token = strtok(NULL, ":");
		if (token == NULL) {
			continue;
		}
		field = field_match(token);
		switch (field) {

		case (-1): {
			if (strlen(token) == 0) {
				break; /*Fine della stringa*/
			}
			return (-1); /*Campo mal formato*/
			break;
		}

		case (AUTORE): {
			token = strtok(NULL, ",");
			break;
		}

		default: {
			token = strtok(NULL, ";");
			break;
		}

		}

		parsed = field_store(field, token, s);
		if (parsed == -1) {
			return (-1);
		}
		fields += parsed;
	}

	/*Alla fine della stringa da parsare si controlla che tutti i campi  obbligatori siano stati riempiti*/
	if (fields < (NCAMPI - 3)) {
		return (-1);
	}
	return (0);
}



/**
 * Funzione di match dei nomi dei campi del record.
 * @param token
 * @return codice del campo corrispondente o codice d'errore.
 */
static int field_match(char *token) {
	int ncampo;
	char *fields[] = {"autore","titolo","editore","luogo_pubblicazione","anno","collocazione","descrizione_fisica","nota","prestito"};
	char *trimmed;

	if (token == NULL) {
		errno = EINVAL;
		return (-1);
	}
	trimmed = trim(token);
	for(ncampo = 0; ncampo < NCAMPI; ncampo++){
		if(strcmp(trimmed,fields[ncampo]) == 0)
			return(ncampo);
	}
	return(-1);
}


/**
 * Controlla la validità del valore per il campo e salva il valore nel corrispondente campo della struct scheda
 * @param field codice del campo
 * @param token token da analizzare
 * @param scheda puntatore alla scheda in cui salvare il valore del campo
 * @return 1 in caso di campo obbligatorio salvato, 0 se campo non obbligatorio, -1 in caso di errore
 */
static int field_store(int field, char *token, scheda_t *scheda) {
	char *tmp;
	char *nome;
	char *cognome;
	autore_t *aut;
	int gg;
	int mm;
	int aaaa;

	/*Di default 1 perchè è il caso più comune, se si aggiunge un autore oltre il primo o un campo opzionale, si azzera*/
	int count = 1;
	if (token == NULL) {
		errno = EINVAL;
		return (-1);
	}

	if (scheda == NULL) {
		errno = EINVAL;
		return (-1);
	}
	/*Per ogni campo disponibile si salva il valore contenuto in token*/
	switch (field) {

	case (AUTORE): {
		/*Si controlla la presenza di autori nella scheda*/
		if (scheda->autore != NULL) {
			count = 0;
		} else {
			count = 1;
		}
		cognome = trim(token);
		if (strlen(cognome) > LCOGN) {
			errno = EINVAL;
			return (-1);
		}
		tmp = strtok(NULL, ";");
		if (tmp == NULL) {
			errno = EINVAL;
			return (-1);
		}
		nome = trim(tmp);
		if (strlen(nome) > LNOME) {
			errno = EINVAL;
			return (-1);
		}
		aut = new_autore(&(scheda->autore), cognome, nome);
		if (aut == NULL) {
			errno = ENOMEM;
			return (-1);
		}
		break;
	}

	case (TITOLO): {
		count = 1;
		tmp = trim(token);
		snprintf(scheda->titolo, LLSTRING, "%s", tmp);
		break;
	}

	case (EDITORE): {
		count = 1;
		tmp = trim(token);
		snprintf(scheda->pub.editore, LSSTRING, "%s", tmp);
		break;
	}

	case (LUOGO_PUBBLICAZIONE): {
		count = 1;
		tmp = trim(token);
		snprintf(scheda->pub.luogo, LSSTRING, "%s", tmp);
		break;
	}

	case (ANNO): {
		count = 1;
		tmp = trim(token);
		if (strlen(tmp) > LANNO) {
			errno = EINVAL;
			return (-1);
		}
		if (sscanf(tmp, "%u", &(scheda->pub.anno)) != 1) {
			return (-1);
		}
		break;
	}

	case (COLLOCAZIONE): {
		count = 0; /*Essendo il campo collocazione facoltativo*/
		tmp = trim(token);
		if (strlen(tmp) > LSSTRING) {
			errno = EINVAL;
			return (-1);
		}
		snprintf(scheda->collocazione, LSSTRING, "%s", tmp);
		break;
	}

	case (DESCRIZIONE_FISICA): {
		count = 1;
		tmp = trim(token);
		if (strlen(tmp) > LSSTRING) {
			errno = EINVAL;
			return (-1);
		}
		snprintf(scheda->descrizione_fisica, LSSTRING, "%s", tmp);
		break;
	}

	case (NOTA): {
		count = 0; /*Essendo il campo nota facoltativo*/
		tmp = trim(token);
		if (strlen(tmp) > LLSTRING) {
			errno = EINVAL;
			return (-1);
		}
		snprintf(scheda->nota, LLSTRING, "%s", tmp);
		break;
	}

	case (PRESTITO): {
		count = 0; /*Essendo il campo prestito facoltativo*/
		scheda->prestito.disponibile = FALSE;
		tmp = trim(token);
		if (sscanf(tmp, "%d-%d-%d", &gg, &mm, &aaaa) != 3) {
			errno = EINVAL;
			return (-1);
		}
		if (gg < 1 || gg > 31) {
			errno = EINVAL;
			return (-1);
		}
		if (mm < 1 || mm > 12) {
			errno = EINVAL;
			return (-1);
		}
		mm--;
		scheda->prestito.scadenza.tm_mday = gg;
		scheda->prestito.scadenza.tm_mon = mm;
		scheda->prestito.scadenza.tm_year = aaaa - 1900;
		break;
	}

	default: {
		return (-1);
	}

	}

	return (count);
}



/**
 * Compara le schede in base agli autori: scorre la lista autori fino a che non trova differenze.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */

static int autore_cmp(const void *p1, const void *p2) {
	const scheda_t *s1 = *(const scheda_t **)p1;
	const scheda_t *s2 = *(const scheda_t **)p2;
	autore_t *a1, *a2;
	int cmp;
	a1 = s1->autore;
	a2 = s2->autore;
	while (a1 != NULL && a2 != NULL) {
		cmp = strcmp(a1->cognome, a2->cognome);
		if (cmp != 0) {
			return (cmp);
		}
		cmp = strcmp(a1->nome, a2->nome);
		if (cmp != 0) {
			return (cmp);
		}

		a1 = a1->next;
		a2 = a2->next;
	}

	if (a1 == NULL && a2 != NULL) {
		return (-1);
	}

	if (a1 != NULL && a2 == NULL) {
		return (1);
	}
	/*Dopo aver scandito entrambe le liste degli autori, con entrambe lo stesso numero di autori*/
	return (0);
}

/**
 * Compara le schede in base al titolo.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int titolo_cmp(const void *p1, const void *p2) {
	const scheda_t *s1 = *(const scheda_t **)p1;
	const scheda_t *s2 = *(const scheda_t **)p2;
	return (strcmp(s1->titolo, s2->titolo));
}

/**
 * Compara le schede in base all'editore.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int editore_cmp(const void *p1, const void *p2) {
	const scheda_t *s1 = *(const scheda_t **)p1;
	const scheda_t *s2 = *(const scheda_t **)p2;
	return (strcmp(s1->pub.editore, s2->pub.editore));
}

/**
 * Compara le schede in base al luogo di pubblicazione.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int luogo_cmp(const void *p1, const void *p2) {
	const scheda_t *s1 = *(const scheda_t **)p1;
	const scheda_t *s2 = *(const scheda_t **)p2;
	return (strcmp(s1->pub.luogo, s2->pub.luogo));
}

/**
 * Compara le schede in base all'anno di pubblicazione.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int anno_cmp(const void *p1, const void *p2) {
	const scheda_t *s1 = *(const scheda_t **)p1;
	const scheda_t *s2 = *(const scheda_t **)p2;
	if (s1->pub.anno == s2->pub.anno) {
		return (0);
	}
	if (s1->pub.anno > s2->pub.anno) {
		return (1);
	}
	return (-1);
}

/**
 * Compara le schede in base alla collocazione.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int colloc_cmp(const void *p1, const void *p2) {
	const scheda_t *s1 = *(const scheda_t **)p1;
	const scheda_t *s2 = *(const scheda_t **)p2;
	return (strcmp(s1->collocazione, s2->collocazione));
}

/**
 * Compara le schede in base alla descrizione.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int descr_cmp(const void *p1, const void *p2) {
	const scheda_t *s1 = *(const scheda_t **)p1;
	const scheda_t *s2 = *(const scheda_t **)p2;
	return (strcmp(s1->descrizione_fisica, s2->descrizione_fisica));
}

/**
 * Compara le schede in base alla nota.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int nota_cmp(const void *p1, const void *p2) {
	const scheda_t *s1 = *(const scheda_t **)p1;
	const scheda_t *s2 = *(const scheda_t **)p2;
	return (strcmp(s1->nota, s2->nota));
}

/**
 * Compara le schede alla data di termine del prestito.
 * @param p1 prima scheda bibliografica da comparare
 * @param p2 seconda scheda bibliografica da comparare
 * @return risultato della comparazione per qsort
 */
static int prestito_cmp(const void *p1, const void *p2) {
	const scheda_t *s1 = *(const scheda_t **)p1;
	const scheda_t *s2 = *(const scheda_t **)p2;
	if (s1->prestito.scadenza.tm_year > s2->prestito.scadenza.tm_year) {
		return (1);
	}
	if (s1->prestito.scadenza.tm_year < s2->prestito.scadenza.tm_year) {
		return (-1);
	}
	/*Stesso anno*/
	if (s1->prestito.scadenza.tm_mon > s2->prestito.scadenza.tm_mon) {
		return (1);
	}
	if (s1->prestito.scadenza.tm_mon < s2->prestito.scadenza.tm_mon) {
		return (-1);
	}
	/*Stesso anno, stesso mese*/
	if (s1->prestito.scadenza.tm_mday > s2->prestito.scadenza.tm_mday) {
		return (1);
	}
	if (s1->prestito.scadenza.tm_mday < s2->prestito.scadenza.tm_mday) {
		return (-1);
	}
	return (0);
}
